<!DOCTYPE html>
<html lang="zh-CN">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="robots" content="noodp" />
        <title>Java动态代理详解 - L_B__</title><meta name="referrer" content="no-referrer">
<meta name="description" content="Java动态代理详解"><meta property="og:title" content="Java动态代理详解" />
<meta property="og:description" content="Java动态代理详解" />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://acking-you.github.io/posts/java%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E8%AF%A6%E8%A7%A3/" /><meta property="og:image" content="https://acking-you.github.io/logo.png"/><meta property="article:section" content="posts" />
<meta property="article:published_time" content="2022-02-20T00:00:00+00:00" />
<meta property="article:modified_time" content="2022-02-20T00:00:00+00:00" />

<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:image" content="https://acking-you.github.io/logo.png"/>

<meta name="twitter:title" content="Java动态代理详解"/>
<meta name="twitter:description" content="Java动态代理详解"/>
<meta name="application-name" content="FeelIt">
<meta name="apple-mobile-web-app-title" content="FeelIt"><meta name="theme-color" content="#ffffff"><meta name="msapplication-TileColor" content="#da532c"><link rel="canonical" href="https://acking-you.github.io/posts/java%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E8%AF%A6%E8%A7%A3/" /><link rel="prev" href="https://acking-you.github.io/posts/pat%E7%94%B2%E7%BA%A7-insertion-or-heap-sort/" /><link rel="next" href="https://acking-you.github.io/posts/1bit%E4%B8%8E2bit%E5%AD%97%E7%AC%A6%E7%AE%80%E5%8D%95%E6%A8%A1%E6%8B%9F%E9%A2%98/" /><link rel="stylesheet" href="/css/page.min.css"><link rel="stylesheet" href="/css/home.min.css"><script type="application/ld+json">
    {
        "@context": "http://schema.org",
        "@type": "BlogPosting",
        "headline": "Java动态代理详解",
        "inLanguage": "zh-CN",
        "mainEntityOfPage": {
            "@type": "WebPage",
            "@id": "https:\/\/acking-you.github.io\/posts\/java%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E8%AF%A6%E8%A7%A3\/"
        },"genre": "posts","keywords": "Java动态代理详解","wordcount":  3319 ,
        "url": "https:\/\/acking-you.github.io\/posts\/java%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E8%AF%A6%E8%A7%A3\/","datePublished": "2022-02-20T00:00:00+00:00","dateModified": "2022-02-20T00:00:00+00:00","publisher": {
            "@type": "Organization",
            "name": "作者"},"author": {
                "@type": "Person",
                "name": "作者"
            },"description": "Java动态代理详解"
    }
    </script></head><body data-header-desktop="auto" data-header-mobile="auto"><script>(window.localStorage && localStorage.getItem('theme') ? localStorage.getItem('theme') === 'dark' : ('auto' === 'auto' ? window.matchMedia('(prefers-color-scheme: dark)').matches : 'auto' === 'dark')) && document.body.setAttribute('theme', 'dark');</script>

        <div id="mask"></div><div class="wrapper"><header class="desktop" id="header-desktop">
    <div class="header-wrapper">
        <div class="header-title">
            <a href="/" title="L_B__">L_B__</a>
        </div>
        <div class="menu">
            <div class="menu-inner"><a class="menu-item" href="/posts/"> 文章 </a><a class="menu-item" href="/tags/"> 标签 </a><a class="menu-item" href="/categories/"> 分类 </a><span class="menu-item delimiter"></span><span class="menu-item search" id="search-desktop">
                        <input type="text" placeholder="搜索文章标题或内容..." id="search-input-desktop">
                        <a href="#" class="search-button search-toggle" id="search-toggle-desktop" title="搜索">
                            <i class="fas fa-search fa-fw"></i>
                        </a>
                        <a href="#" class="search-button search-clear" id="search-clear-desktop" title="清空">
                            <i class="fas fa-times-circle fa-fw"></i>
                        </a>
                        <span class="search-button search-loading" id="search-loading-desktop">
                            <i class="fas fa-spinner fa-fw fa-spin"></i>
                        </span>
                    </span><a href="javascript:void(0);" class="menu-item theme-switch" title="切换主题">
                    <i class="fas fa-adjust fa-fw"></i>
                </a>
            </div>
        </div>
    </div>
</header><header class="mobile" id="header-mobile">
    <div class="header-container">
        <div class="header-wrapper">
            <div class="header-title">
                <a href="/" title="L_B__">L_B__</a>
            </div>
            <div class="menu-toggle" id="menu-toggle-mobile">
                <span></span><span></span><span></span>
            </div>
        </div>
        <div class="menu" id="menu-mobile"><div class="search-wrapper">
                    <div class="search mobile" id="search-mobile">
                        <input type="text" placeholder="搜索文章标题或内容..." id="search-input-mobile">
                        <a href="#" class="search-button search-toggle" id="search-toggle-mobile" title="搜索">
                            <i class="fas fa-search fa-fw"></i>
                        </a>
                        <a href="#" class="search-button search-clear" id="search-clear-mobile" title="清空">
                            <i class="fas fa-times-circle fa-fw"></i>
                        </a>
                        <span class="search-button search-loading" id="search-loading-mobile">
                            <i class="fas fa-spinner fa-fw fa-spin"></i>
                        </span>
                    </div>
                    <a href="#" class="search-cancel" id="search-cancel-mobile">
                        取消
                    </a>
                </div><a class="menu-item" href="/posts/" title="">文章</a><a class="menu-item" href="/tags/" title="">标签</a><a class="menu-item" href="/categories/" title="">分类</a><div class="menu-item"><a href="javascript:void(0);" class="theme-switch" title="切换主题">
                    <i class="fas fa-adjust fa-fw"></i>
                </a>
            </div></div>
    </div>
</header><div class="search-dropdown desktop">
    <div id="search-dropdown-desktop"></div>
</div>
<div class="search-dropdown mobile">
    <div id="search-dropdown-mobile"></div>
</div><main class="main">
                <div class="container"><div class="toc" id="toc-auto">
            <h2 class="toc-title">目录</h2>
            <div class="toc-content" id="toc-content-auto"></div>
        </div><article class="page single" data-toc="disable"><div class="featured-image"><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://img-blog.csdnimg.cn/img_convert/acea126d07748d6630f37b1b481e5d73.png#pic_center"
        data-srcset="https://img-blog.csdnimg.cn/img_convert/acea126d07748d6630f37b1b481e5d73.png#pic_center, https://img-blog.csdnimg.cn/img_convert/acea126d07748d6630f37b1b481e5d73.png#pic_center 1.5x, https://img-blog.csdnimg.cn/img_convert/acea126d07748d6630f37b1b481e5d73.png#pic_center 2x"
        data-sizes="auto"
        alt="https://img-blog.csdnimg.cn/img_convert/acea126d07748d6630f37b1b481e5d73.png#pic_center"
        title="Java动态代理详解" /></div><div class="single-card" data-image="true"><h2 class="single-title animated flipInX">Java动态代理详解</h2><div class="post-meta">
                <div class="post-meta-line"><span class="post-author"><a href="/" title="Author" rel=" author" class="author"><i class="fas fa-user-circle fa-fw"></i>作者</a></span>&nbsp;<span class="post-category">出版于  <a href="/categories/java%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86/"><i class="far fa-folder fa-fw"></i>Java底层原理</a></span></div>
                <div class="post-meta-line"><span><i class="far fa-calendar-alt fa-fw"></i>&nbsp;<time datetime="2022-02-20">2022-02-20</time></span>&nbsp;<span><i class="fas fa-pencil-alt fa-fw"></i>&nbsp;约 3319 字</span>&nbsp;
                    <span><i class="far fa-clock fa-fw"></i>&nbsp;预计阅读 7 分钟</span>&nbsp;</div>
            </div>
            
            <hr><div class="details toc" id="toc-static"  data-kept="">
                    <div class="details-summary toc-title">
                        <span>目录</span>
                        <span><i class="details-icon fas fa-angle-right"></i></span>
                    </div>
                    <div class="details-content toc-content" id="toc-content-static"><nav id="TableOfContents">
  <ul>
    <li><a href="#静态代理介绍">静态代理介绍</a></li>
    <li><a href="#走进动态代理">走进动态代理</a></li>
    <li><a href="#代理的真正作用实践">代理的真正作用（实践）</a>
      <ul>
        <li><a href="#静态代理的作用">静态代理的作用</a></li>
        <li><a href="#动态代理的运用场景减少重复劳作">动态代理的运用场景（减少重复劳作）</a></li>
        <li><a href="#总结">总结</a></li>
      </ul>
    </li>
  </ul>
</nav></div>
                </div><div class="content" id="content"><blockquote>
<p>在介绍动态代理之前，必须先来聊聊静态代理。</p>
</blockquote>
<h2 id="静态代理介绍">静态代理介绍</h2>
<p>假设现在项目经理有一个需求：在项目现有所有类的方法前后打印日志。</p>
<p>你如何在<strong>不修改已有代码的前提下</strong>，完成这个需求？</p>
<p>我首先想到的是静态代理。具体做法是：</p>
<p>一、为现有的每一个类都编写一个<strong>对应的</strong>代理类，并且让它实现和目标类相同的接口（假设都有）</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://img-blog.csdnimg.cn/5823ecb031794ed3ab07751f86843a01.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=,size_20,color_FFFFFF,t_70,g_se,x_16"
        data-srcset="https://img-blog.csdnimg.cn/5823ecb031794ed3ab07751f86843a01.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16, https://img-blog.csdnimg.cn/5823ecb031794ed3ab07751f86843a01.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16 1.5x, https://img-blog.csdnimg.cn/5823ecb031794ed3ab07751f86843a01.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16 2x"
        data-sizes="auto"
        alt="https://img-blog.csdnimg.cn/5823ecb031794ed3ab07751f86843a01.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=,size_20,color_FFFFFF,t_70,g_se,x_16"
        title="继承关系图" /></p>
<p>二、在创建代理对象时，通过构造器塞入一个目标对象，然后在代理对象的方法内部调用目标对象同名方法，并在调用前后打印日志。也就是说，<strong>代理对象 = 增强代码 + 目标对象（原对象）</strong>。有了代理对象后，就不用原对象了</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://img-blog.csdnimg.cn/de0595e428e343e79ca5ced769404987.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=,size_20,color_FFFFFF,t_70,g_se,x_16"
        data-srcset="https://img-blog.csdnimg.cn/de0595e428e343e79ca5ced769404987.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16, https://img-blog.csdnimg.cn/de0595e428e343e79ca5ced769404987.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16 1.5x, https://img-blog.csdnimg.cn/de0595e428e343e79ca5ced769404987.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16 2x"
        data-sizes="auto"
        alt="https://img-blog.csdnimg.cn/de0595e428e343e79ca5ced769404987.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=,size_20,color_FFFFFF,t_70,g_se,x_16"
        title="代理关系图" /></p>
<p><strong>静态代理的缺陷</strong></p>
<p>程序员要手动为每一个目标类编写对应的代理类。如果当前系统已经有成百上千个类，工作量太大了。所以，现在我们的努力方向是：如何少写或者不写代理类，却能完成代理功能？</p>
<p><strong>复习对象的创建</strong></p>
<p>很多初学Java的朋友眼中创建对象的过程</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://img-blog.csdnimg.cn/e5adc285748f4c81a4db99014d55024e.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=,size_20,color_FFFFFF,t_70,g_se,x_16"
        data-srcset="https://img-blog.csdnimg.cn/e5adc285748f4c81a4db99014d55024e.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16, https://img-blog.csdnimg.cn/e5adc285748f4c81a4db99014d55024e.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16 1.5x, https://img-blog.csdnimg.cn/e5adc285748f4c81a4db99014d55024e.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16 2x"
        data-sizes="auto"
        alt="https://img-blog.csdnimg.cn/e5adc285748f4c81a4db99014d55024e.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=,size_20,color_FFFFFF,t_70,g_se,x_16"
        title="范范的过程" /></p>
<p>实际上可以换个角度，也说得通</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://img-blog.csdnimg.cn/378d68e1923e4f988d3905e43c51c1ad.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=,size_20,color_FFFFFF,t_70,g_se,x_16"
        data-srcset="https://img-blog.csdnimg.cn/378d68e1923e4f988d3905e43c51c1ad.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16, https://img-blog.csdnimg.cn/378d68e1923e4f988d3905e43c51c1ad.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16 1.5x, https://img-blog.csdnimg.cn/378d68e1923e4f988d3905e43c51c1ad.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16 2x"
        data-sizes="auto"
        alt="https://img-blog.csdnimg.cn/378d68e1923e4f988d3905e43c51c1ad.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=,size_20,color_FFFFFF,t_70,g_se,x_16"
        title="加入类加载器的过程" /></p>
<p>所谓的Class对象，是Class类的实例，而Class类是描述所有类的，比如Person类，Student类</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://img-blog.csdnimg.cn/f368d10bf4a448f88b8f251ba3018d77.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=,size_20,color_FFFFFF,t_70,g_se,x_16"
        data-srcset="https://img-blog.csdnimg.cn/f368d10bf4a448f88b8f251ba3018d77.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16, https://img-blog.csdnimg.cn/f368d10bf4a448f88b8f251ba3018d77.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16 1.5x, https://img-blog.csdnimg.cn/f368d10bf4a448f88b8f251ba3018d77.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16 2x"
        data-sizes="auto"
        alt="https://img-blog.csdnimg.cn/f368d10bf4a448f88b8f251ba3018d77.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=,size_20,color_FFFFFF,t_70,g_se,x_16"
        title="编译过程" /></p>
<p>可以看出，要创建一个实例，最关键的就是<strong>得到对应的Class对象</strong>。只不过对于初学者来说，new这个关键字配合构造方法，实在太好用了，底层隐藏了太多细节，一句 Person p = new Person();直接把对象返回给你了。我自己刚开始学Java时，也没意识到Class对象的存在。</p>
<p>分析到这里，貌似有了思路：</p>
<p><strong>能否不写代理类，而直接得到代理Class对象，然后根据它创建代理实例（反射）。</strong></p>
<p>Class对象包含了一个类的所有信息，比如构造器、方法、字段等。如果我们不写代理类，这些信息从哪获取呢？苦思冥想，突然灵光一现：代理类和目标类理应实现同一组接口。<strong>之所以实现相同接口，是为了尽可能保证代理对象的内部结构和目标对象一致，这样我们对代理对象的操作最终都可以转移到目标对象身上，代理对象只需专注于增强代码的编写</strong>。还是上面这幅图：</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://img-blog.csdnimg.cn/cde1ecc740f24e7aa57c0c2f924b7654.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=,size_20,color_FFFFFF,t_70,g_se,x_16"
        data-srcset="https://img-blog.csdnimg.cn/cde1ecc740f24e7aa57c0c2f924b7654.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16, https://img-blog.csdnimg.cn/cde1ecc740f24e7aa57c0c2f924b7654.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16 1.5x, https://img-blog.csdnimg.cn/cde1ecc740f24e7aa57c0c2f924b7654.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16 2x"
        data-sizes="auto"
        alt="https://img-blog.csdnimg.cn/cde1ecc740f24e7aa57c0c2f924b7654.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=,size_20,color_FFFFFF,t_70,g_se,x_16"
        title="静态代理过程" /></p>
<p>所以，可以这样说：接口拥有代理对象和目标对象共同的类信息。所以，我们可以从接口那得到理应由代理类提供的信息。但是别忘了，接口是无法创建对象的，怎么办？</p>
<h2 id="走进动态代理">走进动态代理</h2>
<p>JDK提供了<strong>java.lang.reflect.InvocationHandler</strong>接口和 <strong>java.lang.reflect.Proxy类</strong>，这两个类相互配合，入口是Proxy，所以我们先聊它。</p>
<p>Proxy有个静态方法：<code>getProxyClass(ClassLoader, interfaces)</code>，只要你给它传入类加载器和一组接口，它就给你返回代理Class对象。</p>
<p>用通俗的话说，<code>getProxyClass()</code> 这个方法，会从你传入的接口Class中，“拷贝”类结构信息到一个新的Class对象中，但新的Class对象带有构造器，是可以创建对象的。打个比方，一个大内太监（接口Class），空有一身武艺（类信息），但是无法传给后人。现在江湖上有个妙手神医（Proxy类），发明了克隆大法（getProxyClass），不仅能克隆太监的一身武艺，还保留了小DD（构造器）&hellip;（这到底是道德の沦丧，还是人性的扭曲，欢迎走进动态代理）</p>
<p>所以，一旦我们明确接口，完全可以通过接口的Class对象，创建一个代理Class，通过代理Class即可创建代理对象。</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://img-blog.csdnimg.cn/de20b96056b445399cf16d143f52f550.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=,size_20,color_FFFFFF,t_70,g_se,x_16"
        data-srcset="https://img-blog.csdnimg.cn/de20b96056b445399cf16d143f52f550.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16, https://img-blog.csdnimg.cn/de20b96056b445399cf16d143f52f550.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16 1.5x, https://img-blog.csdnimg.cn/de20b96056b445399cf16d143f52f550.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16 2x"
        data-sizes="auto"
        alt="https://img-blog.csdnimg.cn/de20b96056b445399cf16d143f52f550.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=,size_20,color_FFFFFF,t_70,g_se,x_16"
        title="动态代理和静态代理" /></p>
<p>所以，按我理解，Proxy.getProxyClass()这个方法的本质就是：<strong>以Class造Class。</strong></p>
<p>有了Class对象，就很好办了，具体看代码：</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://img-blog.csdnimg.cn/cfd9fee1094e483f8e3737c6283bff38.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=,size_20,color_FFFFFF,t_70,g_se,x_16"
        data-srcset="https://img-blog.csdnimg.cn/cfd9fee1094e483f8e3737c6283bff38.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16, https://img-blog.csdnimg.cn/cfd9fee1094e483f8e3737c6283bff38.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16 1.5x, https://img-blog.csdnimg.cn/cfd9fee1094e483f8e3737c6283bff38.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16 2x"
        data-sizes="auto"
        alt="https://img-blog.csdnimg.cn/cfd9fee1094e483f8e3737c6283bff38.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=,size_20,color_FFFFFF,t_70,g_se,x_16"
        title="简单看下大抵动态代理实现过程" /></p>
<p>根据代理Class的构造器创建对象时，需要传入InvocationHandler。每次调用代理对象的方法，最终都会调用InvocationHandler的invoke()方法：</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://img-blog.csdnimg.cn/774a97d4e67845b08d2442de958be124.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=,size_20,color_FFFFFF,t_70,g_se,x_16"
        data-srcset="https://img-blog.csdnimg.cn/774a97d4e67845b08d2442de958be124.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16, https://img-blog.csdnimg.cn/774a97d4e67845b08d2442de958be124.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16 1.5x, https://img-blog.csdnimg.cn/774a97d4e67845b08d2442de958be124.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16 2x"
        data-sizes="auto"
        alt="https://img-blog.csdnimg.cn/774a97d4e67845b08d2442de958be124.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=,size_20,color_FFFFFF,t_70,g_se,x_16"
        title="调用过程" /></p>
<p>怎么做到的呢？</p>
<p>上面不是说了吗，根据代理Class的构造器创建对象时，需要传入InvocationHandler。<strong>通过构造器传入一个引用，那么必然有个成员变量去接收</strong>。没错，代理对象的内部确实有个成员变量invocationHandler，而且代理对象的每个方法内部都会调用handler.invoke()！</p>
<p>InvocationHandler对象成了代理对象和目标对象的桥梁，不像静态代理这么直接。</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://img-blog.csdnimg.cn/4adcd39a37fd44b08d8467e0a7706d84.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=,size_20,color_FFFFFF,t_70,g_se,x_16"
        data-srcset="https://img-blog.csdnimg.cn/4adcd39a37fd44b08d8467e0a7706d84.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16, https://img-blog.csdnimg.cn/4adcd39a37fd44b08d8467e0a7706d84.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16 1.5x, https://img-blog.csdnimg.cn/4adcd39a37fd44b08d8467e0a7706d84.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16 2x"
        data-sizes="auto"
        alt="https://img-blog.csdnimg.cn/4adcd39a37fd44b08d8467e0a7706d84.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=,size_20,color_FFFFFF,t_70,g_se,x_16"
        title="关系图" /></p>
<p>大家仔细看上图右侧的动态代理，我在invocationHandler的invoke()方法中并没有写目标对象。因为一开始invocationHandler的invoke()里确实没有目标对象，需要我们手动new。</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://img-blog.csdnimg.cn/23aa4672197b472ea9632cfad11efda9.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=,size_20,color_FFFFFF,t_70,g_se,x_16"
        data-srcset="https://img-blog.csdnimg.cn/23aa4672197b472ea9632cfad11efda9.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16, https://img-blog.csdnimg.cn/23aa4672197b472ea9632cfad11efda9.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16 1.5x, https://img-blog.csdnimg.cn/23aa4672197b472ea9632cfad11efda9.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16 2x"
        data-sizes="auto"
        alt="https://img-blog.csdnimg.cn/23aa4672197b472ea9632cfad11efda9.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=,size_20,color_FFFFFF,t_70,g_se,x_16"
        title="调用过程" /></p>
<p>但这种写法不够优雅，属于硬编码。我这次代理A对象，下次想代理B对象还要进来改invoke()方法，太差劲了。改进一下，让调用者把目标对象作为参数传进来：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">ProxyTest</span> <span class="o">{</span>
	<span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">Throwable</span> <span class="o">{</span>
		<span class="n">CalculatorImpl</span> <span class="n">target</span> <span class="o">=</span> <span class="k">new</span> <span class="n">CalculatorImpl</span><span class="o">();</span>
                <span class="c1">//传入目标对象
</span><span class="c1"></span>                <span class="c1">//目的：1.根据它实现的接口生成代理对象 2.代理对象调用目标对象方法
</span><span class="c1"></span>		<span class="n">Calculator</span> <span class="n">calculatorProxy</span> <span class="o">=</span> <span class="o">(</span><span class="n">Calculator</span><span class="o">)</span> <span class="n">getProxy</span><span class="o">(</span><span class="n">target</span><span class="o">);</span>
		<span class="n">calculatorProxy</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">1</span><span class="o">,</span> <span class="n">2</span><span class="o">);</span>
		<span class="n">calculatorProxy</span><span class="o">.</span><span class="na">subtract</span><span class="o">(</span><span class="n">2</span><span class="o">,</span> <span class="n">1</span><span class="o">);</span>
	<span class="o">}</span>

	<span class="kd">private</span> <span class="kd">static</span> <span class="n">Object</span> <span class="nf">getProxy</span><span class="o">(</span><span class="kd">final</span> <span class="n">Object</span> <span class="n">target</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">Exception</span> <span class="o">{</span>
		<span class="c1">//参数1：随便找个类加载器给它， 参数2：目标对象实现的接口，让代理对象实现相同接口
</span><span class="c1"></span>		<span class="n">Class</span> <span class="n">proxyClazz</span> <span class="o">=</span> <span class="n">Proxy</span><span class="o">.</span><span class="na">getProxyClass</span><span class="o">(</span><span class="n">target</span><span class="o">.</span><span class="na">getClass</span><span class="o">().</span><span class="na">getClassLoader</span><span class="o">(),</span> <span class="n">target</span><span class="o">.</span><span class="na">getClass</span><span class="o">().</span><span class="na">getInterfaces</span><span class="o">());</span>
		<span class="n">Constructor</span> <span class="n">constructor</span> <span class="o">=</span> <span class="n">proxyClazz</span><span class="o">.</span><span class="na">getConstructor</span><span class="o">(</span><span class="n">InvocationHandler</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
		<span class="n">Object</span> <span class="n">proxy</span> <span class="o">=</span> <span class="n">constructor</span><span class="o">.</span><span class="na">newInstance</span><span class="o">(</span><span class="k">new</span> <span class="n">InvocationHandler</span><span class="o">()</span> <span class="o">{</span>
			<span class="nd">@Override</span>
			<span class="kd">public</span> <span class="n">Object</span> <span class="nf">invoke</span><span class="o">(</span><span class="n">Object</span> <span class="n">proxy</span><span class="o">,</span> <span class="n">Method</span> <span class="n">method</span><span class="o">,</span> <span class="n">Object</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">Throwable</span> <span class="o">{</span>
				<span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">method</span><span class="o">.</span><span class="na">getName</span><span class="o">()</span> <span class="o">+</span> <span class="s">&#34;方法开始执行...&#34;</span><span class="o">);</span>
				<span class="n">Object</span> <span class="n">result</span> <span class="o">=</span> <span class="n">method</span><span class="o">.</span><span class="na">invoke</span><span class="o">(</span><span class="n">target</span><span class="o">,</span> <span class="n">args</span><span class="o">);</span>
				<span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">result</span><span class="o">);</span>
				<span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">method</span><span class="o">.</span><span class="na">getName</span><span class="o">()</span> <span class="o">+</span> <span class="s">&#34;方法执行结束...&#34;</span><span class="o">);</span>
				<span class="k">return</span> <span class="n">result</span><span class="o">;</span>
			<span class="o">}</span>
		<span class="o">});</span>
		<span class="k">return</span> <span class="n">proxy</span><span class="o">;</span>
	<span class="o">}</span>
<span class="o">}</span>
</code></pre></div><p>这样就非常灵活，非常优雅了。无论现在系统有多少类，只要你把实例传进来，getProxy()都能给你返回对应的代理对象。就这样，我们完美地跳过了代理类，直接创建了代理对象！</p>
<p>不过实际编程中，一般不用getProxyClass()，而是使用Proxy类的另一个静态方法：Proxy.newProxyInstance()，直接返回代理实例，连中间得到代理Class对象的过程都帮你隐藏：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">ProxyTest</span> <span class="o">{</span>
	<span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">Throwable</span> <span class="o">{</span>
		<span class="n">CalculatorImpl</span> <span class="n">target</span> <span class="o">=</span> <span class="k">new</span> <span class="n">CalculatorImpl</span><span class="o">();</span>
		<span class="n">Calculator</span> <span class="n">calculatorProxy</span> <span class="o">=</span> <span class="o">(</span><span class="n">Calculator</span><span class="o">)</span> <span class="n">getProxy</span><span class="o">(</span><span class="n">target</span><span class="o">);</span>
		<span class="n">calculatorProxy</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">1</span><span class="o">,</span> <span class="n">2</span><span class="o">);</span>
		<span class="n">calculatorProxy</span><span class="o">.</span><span class="na">subtract</span><span class="o">(</span><span class="n">2</span><span class="o">,</span> <span class="n">1</span><span class="o">);</span>
	<span class="o">}</span>

	<span class="kd">private</span> <span class="kd">static</span> <span class="n">Object</span> <span class="nf">getProxy</span><span class="o">(</span><span class="kd">final</span> <span class="n">Object</span> <span class="n">target</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">Exception</span> <span class="o">{</span>
		<span class="n">Object</span> <span class="n">proxy</span> <span class="o">=</span> <span class="n">Proxy</span><span class="o">.</span><span class="na">newProxyInstance</span><span class="o">(</span>
				<span class="n">target</span><span class="o">.</span><span class="na">getClass</span><span class="o">().</span><span class="na">getClassLoader</span><span class="o">(),</span><span class="cm">/*类加载器*/</span>
				<span class="n">target</span><span class="o">.</span><span class="na">getClass</span><span class="o">().</span><span class="na">getInterfaces</span><span class="o">(),</span><span class="cm">/*让代理对象和目标对象实现相同接口*/</span>
				<span class="k">new</span> <span class="n">InvocationHandler</span><span class="o">(){</span><span class="cm">/*代理对象的方法最终都会被JVM导向它的invoke方法*/</span>
					<span class="kd">public</span> <span class="n">Object</span> <span class="nf">invoke</span><span class="o">(</span><span class="n">Object</span> <span class="n">proxy</span><span class="o">,</span> <span class="n">Method</span> <span class="n">method</span><span class="o">,</span> <span class="n">Object</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">Throwable</span> <span class="o">{</span>
						<span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">method</span><span class="o">.</span><span class="na">getName</span><span class="o">()</span> <span class="o">+</span> <span class="s">&#34;方法开始执行...&#34;</span><span class="o">);</span>
						<span class="n">Object</span> <span class="n">result</span> <span class="o">=</span> <span class="n">method</span><span class="o">.</span><span class="na">invoke</span><span class="o">(</span><span class="n">target</span><span class="o">,</span> <span class="n">args</span><span class="o">);</span>
						<span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">result</span><span class="o">);</span>
						<span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">method</span><span class="o">.</span><span class="na">getName</span><span class="o">()</span> <span class="o">+</span> <span class="s">&#34;方法执行结束...&#34;</span><span class="o">);</span>
						<span class="k">return</span> <span class="n">result</span><span class="o">;</span>
					<span class="o">}</span>
				<span class="o">}</span>
		<span class="o">);</span>
		<span class="k">return</span> <span class="n">proxy</span><span class="o">;</span>
	<span class="o">}</span>
<span class="o">}</span>
</code></pre></div><p>现在，我想大家应该能看懂动态代理了。</p>
<p>那么来张小图做总结：</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://img-blog.csdnimg.cn/9a91618ba36e4d4293f46324d5d46b78.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=,size_20,color_FFFFFF,t_70,g_se,x_16"
        data-srcset="https://img-blog.csdnimg.cn/9a91618ba36e4d4293f46324d5d46b78.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16, https://img-blog.csdnimg.cn/9a91618ba36e4d4293f46324d5d46b78.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16 1.5x, https://img-blog.csdnimg.cn/9a91618ba36e4d4293f46324d5d46b78.png?x-oss-process=image/watermark%2ctype_d3F5LXplbmhlaQ%2cshadow_50%2ctext_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=%2csize_20%2ccolor_FFFFFF%2ct_70%2cg_se%2cx_16 2x"
        data-sizes="auto"
        alt="https://img-blog.csdnimg.cn/9a91618ba36e4d4293f46324d5d46b78.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQysrKysrKysrKysrKysrKysrKys=,size_20,color_FFFFFF,t_70,g_se,x_16"
        title="小对比" /></p>
<h2 id="代理的真正作用实践">代理的真正作用（实践）</h2>
<h3 id="静态代理的作用">静态代理的作用</h3>
<p>假如我们有一个字体提供类，有多种实现（从磁盘，从网络，从系统）</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="kd">public</span> <span class="kd">interface</span> <span class="nc">FontProvider</span> <span class="o">{</span>
    <span class="n">Font</span> <span class="nf">getFont</span><span class="o">(</span><span class="n">String</span> <span class="n">name</span><span class="o">);</span>
<span class="o">}</span>

<span class="kd">public</span> <span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">ProviderFactory</span> <span class="o">{</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="n">FontProvider</span> <span class="nf">getFontProvider</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="k">new</span> <span class="n">FontProviderFromDisk</span><span class="o">();</span>
    <span class="o">}</span>
<span class="o">}</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nf">Main</span><span class="o">()</span> <span class="o">{</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">FontProvider</span> <span class="n">fontProvider</span> <span class="o">=</span> <span class="n">ProviderFactory</span><span class="o">.</span><span class="na">getFontProvider</span><span class="o">();</span>
        <span class="n">Font</span> <span class="n">font</span> <span class="o">=</span> <span class="n">fontProvider</span><span class="o">.</span><span class="na">getFont</span><span class="o">(</span><span class="s">&#34;微软雅黑&#34;</span><span class="o">);</span>
        <span class="o">......</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div><p>现在我们希望给他加上一个缓存功能，我们可以用静态代理来完成。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">CachedFontProvider</span> <span class="kd">implements</span> <span class="n">FontProvider</span> <span class="o">{</span>
    <span class="kd">private</span> <span class="n">FontProvider</span> <span class="n">fontProvider</span><span class="o">;</span>
    <span class="kd">private</span> <span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="o">,</span> <span class="n">Font</span><span class="o">&gt;</span> <span class="n">cached</span><span class="o">;</span>

    <span class="kd">public</span> <span class="nf">CachedFontProvider</span><span class="o">(</span><span class="n">FontProvider</span> <span class="n">fontProvider</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">fontProvider</span> <span class="o">=</span> <span class="n">fontProvider</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="n">Font</span> <span class="nf">getFont</span><span class="o">(</span><span class="n">String</span> <span class="n">name</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">Font</span> <span class="n">font</span> <span class="o">=</span> <span class="n">cached</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">name</span><span class="o">);</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">font</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">font</span> <span class="o">=</span> <span class="n">fontProvider</span><span class="o">.</span><span class="na">getFont</span><span class="o">(</span><span class="n">name</span><span class="o">);</span>
            <span class="n">cached</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">name</span><span class="o">,</span> <span class="n">font</span><span class="o">);</span>
        <span class="o">}</span>
        <span class="k">return</span> <span class="n">font</span><span class="o">;</span>
    <span class="o">}</span>
<span class="o">}</span>


<span class="cm">/* 对工厂类进行相应修改，代码使用处不必进行任何修改。
</span><span class="cm">   这也是面向接口编程以及工厂模式的一个好处 */</span>
<span class="kd">public</span> <span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">ProviderFactory</span> <span class="o">{</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="n">FontProvider</span> <span class="nf">getFontProvider</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="k">new</span> <span class="n">CachedFontProvider</span><span class="o">(</span><span class="k">new</span> <span class="n">FontProviderFromDisk</span><span class="o">());</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div><p>当然，我们直接修改FontProviderFromDisk类也可以实现目的，但是我们还有FontProviderFromNet, FontProviderFromSystem等多种实现类，一一修改太过繁琐且易出错。</p>
<p>况且将来还可能添加日志，权限检查，异常处理等功能显然用代理类更好一点。</p>
<h3 id="动态代理的运用场景减少重复劳作">动态代理的运用场景（减少重复劳作）</h3>
<p>比如：考虑以下各种情况，有多个提供类，每个类都有getXxx(String name)方法，每个类都要加入缓存功能，使用静态代理虽然也能实现，但是也是略显繁琐，需要手动一一创建代理类。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="kd">public</span> <span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">ProviderFactory</span> <span class="o">{</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="n">FontProvider</span> <span class="nf">getFontProvider</span><span class="o">()</span> <span class="o">{...}</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="n">ImageProvider</span> <span class="nf">getImageProvider</span><span class="o">()</span> <span class="o">{...}</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="n">MusicProvider</span> <span class="nf">getMusicProvider</span><span class="o">()</span> <span class="o">{...}</span>
    <span class="o">......</span>
<span class="o">}</span>
</code></pre></div><p>使用动态代理怎么完成呢？(只需一份缓存版本实现即可)</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">CachedProviderHandler</span> <span class="kd">implements</span> <span class="n">InvocationHandler</span> <span class="o">{</span>
    <span class="kd">private</span> <span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="o">,</span> <span class="n">Object</span><span class="o">&gt;</span> <span class="n">cached</span> <span class="o">=</span> <span class="k">new</span> <span class="n">HashMap</span><span class="o">&lt;&gt;();</span>
    <span class="kd">private</span> <span class="n">Object</span> <span class="n">target</span><span class="o">;</span>

    <span class="kd">public</span> <span class="nf">CachedProviderHandler</span><span class="o">(</span><span class="n">Object</span> <span class="n">target</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">target</span> <span class="o">=</span> <span class="n">target</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="n">Object</span> <span class="nf">invoke</span><span class="o">(</span><span class="n">Object</span> <span class="n">proxy</span><span class="o">,</span> <span class="n">Method</span> <span class="n">method</span><span class="o">,</span> <span class="n">Object</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span>
        <span class="kd">throws</span> <span class="n">Throwable</span> <span class="o">{</span>
        <span class="n">Type</span><span class="o">[]</span> <span class="n">types</span> <span class="o">=</span> <span class="n">method</span><span class="o">.</span><span class="na">getParameterTypes</span><span class="o">();</span>
        <span class="c1">//关键逻辑：当调用的是get方法时，我们就来发挥缓冲区的作用
</span><span class="c1"></span>        <span class="k">if</span> <span class="o">(</span><span class="n">method</span><span class="o">.</span><span class="na">getName</span><span class="o">().</span><span class="na">matches</span><span class="o">(</span><span class="s">&#34;get.+&#34;</span><span class="o">)</span> <span class="o">&amp;&amp;</span> <span class="o">(</span><span class="n">types</span><span class="o">.</span><span class="na">length</span> <span class="o">==</span> <span class="n">1</span><span class="o">)</span> <span class="o">&amp;&amp;</span>
                <span class="o">(</span><span class="n">types</span><span class="o">[</span><span class="n">0</span><span class="o">]</span> <span class="o">==</span> <span class="n">String</span><span class="o">.</span><span class="na">class</span><span class="o">))</span> <span class="o">{</span>
            <span class="n">String</span> <span class="n">key</span> <span class="o">=</span> <span class="o">(</span><span class="n">String</span><span class="o">)</span> <span class="n">args</span><span class="o">[</span><span class="n">0</span><span class="o">];</span>
            <span class="n">Object</span> <span class="n">value</span> <span class="o">=</span> <span class="n">cached</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">key</span><span class="o">);</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">value</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
                <span class="n">value</span> <span class="o">=</span> <span class="n">method</span><span class="o">.</span><span class="na">invoke</span><span class="o">(</span><span class="n">target</span><span class="o">,</span> <span class="n">args</span><span class="o">);</span>
                <span class="n">cached</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">key</span><span class="o">,</span> <span class="n">value</span><span class="o">);</span>
            <span class="o">}</span>
            <span class="k">return</span> <span class="n">value</span><span class="o">;</span>
        <span class="o">}</span>
        <span class="k">return</span> <span class="n">method</span><span class="o">.</span><span class="na">invoke</span><span class="o">(</span><span class="n">target</span><span class="o">,</span> <span class="n">args</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span>

<span class="kd">public</span> <span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">ProviderFactory</span> <span class="o">{</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="n">FontProvider</span> <span class="nf">getFontProvider</span><span class="o">()</span> <span class="o">{</span>
        <span class="n">Class</span><span class="o">&lt;</span><span class="n">FontProvider</span><span class="o">&gt;</span> <span class="n">targetClass</span> <span class="o">=</span> <span class="n">FontProvider</span><span class="o">.</span><span class="na">class</span><span class="o">;</span>
        <span class="k">return</span> <span class="o">(</span><span class="n">FontProvider</span><span class="o">)</span> <span class="n">Proxy</span><span class="o">.</span><span class="na">newProxyInstance</span><span class="o">(</span><span class="n">targetClass</span><span class="o">.</span><span class="na">getClassLoader</span><span class="o">(),</span>
            <span class="k">new</span> <span class="n">Class</span><span class="o">[]</span> <span class="o">{</span> <span class="n">targetClass</span> <span class="o">},</span>
            <span class="k">new</span> <span class="n">CachedProviderHandler</span><span class="o">(</span><span class="k">new</span> <span class="n">FontProviderFromDisk</span><span class="o">()));</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div><h3 id="总结">总结</h3>
<p>很明显，动态代理在需要大量静态代理的情况下，大大减少了重复劳作，动态代理yyds！(不知道C++如何去实现动态代理。。)</p>
</div><div class="post-footer" id="post-footer">
    <div class="post-info"><div class="post-info-tag"><span><a href="/tags/java%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E8%AF%A6%E8%A7%A3/">Java动态代理详解</a>
                </span></div><div class="post-info-line"><div class="post-info-mod">
                <span>更新于 2022-02-20</span>
            </div><div class="post-info-mod"></div>
        </div><div class="post-info-share">
            <span><a href="javascript:void(0);" title="分享到 Twitter" data-sharer="twitter" data-url="https://acking-you.github.io/posts/java%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E8%AF%A6%E8%A7%A3/" data-title="Java动态代理详解" data-hashtags="Java动态代理详解"><i class="fab fa-twitter fa-fw"></i></a><a href="javascript:void(0);" title="分享到 Facebook" data-sharer="facebook" data-url="https://acking-you.github.io/posts/java%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E8%AF%A6%E8%A7%A3/" data-hashtag="Java动态代理详解"><i class="fab fa-facebook-square fa-fw"></i></a><a href="javascript:void(0);" title="分享到 WhatsApp" data-sharer="whatsapp" data-url="https://acking-you.github.io/posts/java%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E8%AF%A6%E8%A7%A3/" data-title="Java动态代理详解" data-web><i class="fab fa-whatsapp fa-fw"></i></a><a href="javascript:void(0);" title="分享到 Line" data-sharer="line" data-url="https://acking-you.github.io/posts/java%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E8%AF%A6%E8%A7%A3/" data-title="Java动态代理详解"><i class="fab fa-line fa-fw"></i></a><a href="javascript:void(0);" title="分享到 微博" data-sharer="weibo" data-url="https://acking-you.github.io/posts/java%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E8%AF%A6%E8%A7%A3/" data-title="Java动态代理详解" data-image="https://img-blog.csdnimg.cn/img_convert/acea126d07748d6630f37b1b481e5d73.png#pic_center"><i class="fab fa-weibo fa-fw"></i></a><a href="javascript:void(0);" title="分享到 Myspace" data-sharer="myspace" data-url="https://acking-you.github.io/posts/java%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E8%AF%A6%E8%A7%A3/" data-title="Java动态代理详解" data-description="Java动态代理详解"><i data-svg-src="/lib/simple-icons/icons/myspace.min.svg"></i></a><a href="javascript:void(0);" title="分享到 Blogger" data-sharer="blogger" data-url="https://acking-you.github.io/posts/java%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E8%AF%A6%E8%A7%A3/" data-title="Java动态代理详解" data-description="Java动态代理详解"><i class="fab fa-blogger fa-fw"></i></a><a href="javascript:void(0);" title="分享到 Evernote" data-sharer="evernote" data-url="https://acking-you.github.io/posts/java%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E8%AF%A6%E8%A7%A3/" data-title="Java动态代理详解"><i class="fab fa-evernote fa-fw"></i></a></span>
        </div></div><div class="post-nav"><a href="/posts/pat%E7%94%B2%E7%BA%A7-insertion-or-heap-sort/" class="prev" rel="prev" title="PAT甲级--Insertion-or-Heap-Sort"><i class="fas fa-angle-left fa-fw"></i>Previous Post</a>
            <a href="/posts/1bit%E4%B8%8E2bit%E5%AD%97%E7%AC%A6%E7%AE%80%E5%8D%95%E6%A8%A1%E6%8B%9F%E9%A2%98/" class="next" rel="next" title="1bit与2bit字符——简单模拟题">Next Post<i class="fas fa-angle-right fa-fw"></i></a></div></div>
</div></article></div>
            </main>
            <footer class="footer"><div class="footer-container"><div class="footer-line">由 <a href="https://gohugo.io/" target="_blank" rel="noopener noreffer" title="Hugo 0.86.0">Hugo</a> 强力驱动 | 主题 - <a href="https://github.com/khusika/FeelIt" target="_blank" rel="noopener noreffer" title="FeelIt 1.0.1"><i class="fas fa-hand-holding-heart fa-fw"></i> FeelIt</a>
        </div><div class="footer-line" itemscope itemtype="http://schema.org/CreativeWork"><i class="far fa-copyright fa-fw"></i><span itemprop="copyrightYear">2023</span><span class="author" itemprop="copyrightHolder">&nbsp;<a href="/"></a></span></div>
</div>
</footer>
        </div>

        <div id="fixed-buttons"><a href="#" id="back-to-top" class="fixed-button" title="回到顶部">
                <i class="fas fa-chevron-up fa-fw"></i>
            </a></div><link rel="stylesheet" href="/lib/fontawesome-free/all.min.css"><link rel="stylesheet" href="/lib/animate/animate.min.css"><link rel="stylesheet" href="/lib/katex/katex.min.css"><link rel="stylesheet" href="/lib/katex/copy-tex.min.css"><script src="/lib/autocomplete/autocomplete.min.js"></script><script src="/lib/lunr/lunr.min.js"></script><script src="/lib/lunr/lunr.stemmer.support.min.js"></script><script src="/lib/lunr/lunr.zh.min.js"></script><script src="/lib/lazysizes/lazysizes.min.js"></script><script src="/lib/clipboard/clipboard.min.js"></script><script src="/lib/sharer/sharer.min.js"></script><script src="/lib/katex/katex.min.js"></script><script src="/lib/katex/auto-render.min.js"></script><script src="/lib/katex/copy-tex.min.js"></script><script src="/lib/katex/mhchem.min.js"></script><script>window.config={"code":{"copyTitle":"复制到剪贴板","maxShownLines":200},"comment":{},"math":{"delimiters":[{"display":true,"left":"$$","right":"$$"},{"display":true,"left":"\\[","right":"\\]"},{"display":false,"left":"$","right":"$"},{"display":false,"left":"\\(","right":"\\)"}],"strict":false},"search":{"highlightTag":"em","lunrIndexURL":"/index.json","lunrLanguageCode":"zh","lunrSegmentitURL":"/lib/lunr/lunr.segmentit.js","maxResultLength":100,"noResultsFound":"没有找到结果","snippetLength":50,"type":"lunr"}};</script><script src="/js/theme.min.js"></script></body></html>
